
//秉行：客户使用不规范时，应当出现报错，如果编译通过，理应输出正确的逻辑和效果


//1、外覆类型（wrapper）提醒调用者传参错误检查，将参数的附加条件限制在类型本身

// 2、从语法层面限制调用者不能做的事
// operator*的返回类型上加上const修饰，防止无意的错误赋值if (a * b = c)

// 3、接口应表现出与内置类型的一致性
// 如自定义容器的接口在命名上和STL应具备一致性，或者两个对象相乘，最好重载operator*而不是设计名为”multiply”的成员函数。

// 4、从语法层面限制调用者必须做的事
// 如条款13在获取资源时返回的是原始指针，如果客户忘记使用智能指针进行管理呢？最好的做法是令获取资源的接口直接返回智能指针。
// 对象在动态连接程序库（DLL）被 new 创建，却在另一个 DLL 内被另一个delete销毁。shared_ptr 保证对象会使用原来所在单元的 delete


#include<iostream>
#include<memory>

//考虑一种情形：

class Date
{
    public:
        Date(int day,int month,int year):year_(year),month_(month),day_(day){};

        friend std::ostream& operator<<(std::ostream &cout,const Date &date);
    private:
    int year_,month_,day_;
};

std::ostream& operator<<(std::ostream &cout,const Date &date)
{
    cout << date.year_<<"-"<<date.month_<<"-"<<date.day_<<std::endl;
    return cout;
};

//第二种情形：
struct Month
{
    explicit Month(int month):val_(month){};
    int val_;
};

struct Day
{
    explicit Day(int day):val_(day){};
    int val_;
};

struct Year
{
    explicit Year(int year):val_(year){};
    int val_;
};

class Date_2
{
    public:
        explicit Date_2(const Day &day,const Month &month,const Year &year):day_(day),month_(month),year_(year){};
    private:
        Day day_;
        Month month_;
        Year year_;
};  

//第三种情形：限制取值
struct Month_2
{
    enum E_MON{JAN = 1, FEC, MAR, APR, MAY, JUN, JUL, AGU, SEP, OCT, NOV, DEC};
    explicit Month_2(const E_MON month):month_(month) {}
private:
    int month_;
};

//更好的做法
struct Month_3
{
    static Month_3 Jan(){return Month_3(1);};
    static Month_3 Fec(){return Month_3(2);};
    //...
    private:
        explicit Month_3(const int &month);
};


class Investment{};

//提供一个智能指针接口
std::shared_ptr<Investment> createInvestment()
{
    return (std::make_shared<Investment>());
}


//提供一个指定删除器的智能指针接口：防止客户使用delete作为删除器
void getRidofInvestment(const Investment* ){};

std::shared_ptr<Investment> createInvestment1()
{
    std::shared_ptr<Investment> retVal(static_cast<Investment*>(0), getRidofInvestment);
	//因为0不是指针，需要强转
	//getRidofInvestment函数作为删除器
	//retVal = ...; //令retVal指向正确对象
	return retVal;
}


int main(int argc, char const *argv[])
{
    Date date(16,10,2024);//正确
    Date date1(10,16,2024);//逻辑不正确，但是不报错

    Date_2(Day(16),Month(10),Year(2024));//正确 顺序换了就报错 但是没有限制取值
    std::cout << date;
    return 0;
}
